Example 17-2 : Master File /var/sysgen/rap for RAP-10 Driver
* * rap - Roland RAP-10 Musical Board * * $Revision: 1.1 $ * *FLAG PREFIX SOFT #DEV DEPENDENCIES c rap 61 - $$$
Example 17-3 : Configuration File /var/sysgen/rap.sm for RAP-10 Driver
VECTOR: bustype=EISA module=rap ctlr=0 adapter=0 iospace=(EISAIO,0x330,16) probe_space=(EISAIO,0x330,1)
Example 17-4 : Installation Script for RAP-10 Driver
#!/bin/csh if [ `whoami` != "root" ] then echo "You must be root to run this script.\n" exit 1 fi echo "cp rap.o /var/sysgen/boot/rap.o\n" cp rap.o /var/sysgen/boot/rap.o echo "cp rap.master /var/sysgen/master.d/rap\n" cp rap.master /var/sysgen/master.d/rap echo "cp rap.sm /var/sysgen/system/rap.sm" cp rap.sm /var/sysgen/system/rap.sm echo "mknod /dev/rap c 62 0\n" mknod /dev/rap c 62 0 echo "Make a new kernel anytime by typing: autoconfig -f -v\n"
Example 17-5 : Program to Test RAP-10 Driver
#include <stdio.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <signal.h> #include "rap.h" /* * record.c * * This program plays song from a previuosly recorded file * using RAP-10 board. * */ #define BUF_SIZE 4096 #define FILE_HDR "RAP-10 WAVE FILE" #define RAP_FILE "/dev/rap" #define MAX_BUF 10 #define FOREVER for(;;) uchar_t buf[BUF_SIZE]; uchar_t *fname; void endProg( int ); main (int argc, char **argv) { register int fd, rapfd, bytes; if ( argc <= 1 ) { printf ("play: Usage: play <file_name>\n"); exit(0); } fname = argv[1]; printf ("play: opening file %s\n", fname); fd = open (fname, O_RDONLY); if ( fd == -1 ) { printf ("play: Cannot create file, errno = %d\n", errno); close(rapfd); exit(0); } printf ("play: Checking RAP-10 File ID\n"); if ( read(fd, buf, strlen(FILE_HDR)) <= 0 ) { printf ("play: Could not read the file ID, errno = %d\n", errno); close(fd); exit(0); } if ( strcmp(buf, FILE_HDR) ) { printf ("play: File is not a RAP file\n"); close(fd); exit(0); } printf ("play: opening RAP card\n"); rapfd = open (RAP_FILE, O_WRONLY); if ( rapfd <= 0 ) { printf ("play: Cannot open RAP card, errno = %d\n", errno); exit(0); } printf ("play: Playing ..please wait\n"); /* ignore Interrupt */ sigset (SIGINT, SIG_IGN ); FOREVER { bytes = read(fd, buf, BUF_SIZE); if ( bytes < 0 ) { printf ("play: error reading data, errno = %d", errno); close(fd); close(rapfd); exit(0); } if ( bytes == 0 ) break; bytes = write(rapfd, buf, BUF_SIZE); if ( bytes <= 0 ) { printf ("play: Cannot read from RAP, errno = %d\n", errno); close (rapfd); close (fd); exit(0); } } printf ("play: waiting for Play to End\n"); if ( ioctl (rapfd, RAPIOCTL_END_PLAY) ) { printf ("play: Ioctl error %d", errno ); } else printf ("play: Song succesfully played\n"); close(rapfd); close (fd); }
Example 17-6 : Complete EISA Character Driver for RAP-10
/***************************************************************************** * * Roland RAP-10 Music Card Device Driver for Eisa Bus * --------------------------------------------------- * * INTRODUCTION: * ------------- * This file contains the device driver for Roland RAP-10 * Music Card. Currently it contains necessary routines to Record and * Playback a Wave file. The MIDI Implementation is to be defined and * implemented at later time. * * DESIGN OVERVIEW: * ---------------- * We will use DMA for wave data movements. At any given time, the card * can be either playing or recording and both operations are not allowed. * Also no more than one process at a time can access the card. * * Circular Buffers: * ----------------- * Since DMA operation is performed independently of the processor, * we will buffer the user's data and release the user's process to * do other things (i.e. preparing more data). Internally we use a * circular queue (rwQue) to store the data to be played or recorded. * Each entry in this queue is of the type rwBuf_t where the data will * be stored. Each entry can store up to RW_BUF_SIZE bytes of data. * At the init time, we try to allocate two DMA channels for the card: * Channel 5 and 6. If we can only allocate Channel 5, we will use the * card in Mono mode, otherwise, we will use it as Stereo. DMA has two * buffers of its own: dmaRigh[] and dmaLeft[] for each Channel. For * Stereo play, the data user provides us is of the format: * * <Left Byte><Right Byte><Left Byte><Right Byte>..... * * So for playing, we have to move all Left_Bytes to dmaLeft buffer * and all Right_Bytes to the dmaRight buffer (in Stereo mode only). * In mono mode, we will use dmaLeft[] buffer and all the user's data * are moved to dmaLeft[]. * * The basic operation of the Card are as follow: * * Playing: * -------- * For playing wave data, the user must first open the card through * open() system call.The call comes to us as rapopen(). This * routine resets all global values, states and counters, prepares * necessary DMA structures for each channel, disables RAP-10 * interrupts and establishes this process as the owner of the card. * * The user provides us with the wave data by issuing write() * system calls. This call comes to us as rapwrite(). We will * move the data from user's address space into an empty rwQue[] * entry and will retrun so that the user can issue another call. * If there is no DMA going, we will start one and the data will * start to be moved to the Card to be played. * The user can issue as many write() as necessary. The playing * operation will be done by either closing the card or issuing * an Ioctl call. Issuing Ioctl, will leave this process as owner * still while closing the card will release the card. * * Recording: * ---------- * Assuming that the user has opened the card and is the current * owner, user will issue read() system call. The call comes to * us as rapread(). If no DMA Record is going on, we will start * one. We will move data from rwQue[] entries (as they are filled) * to user's address space. The recording is done either by a * close() or ioctl() call. * * DMA Starting: * ------------- * For Playing, we will start DMA when we have a full circular buffer. * This is done so that we have enough data available for a fast DMA * operation to be busy with. For recording, we will start DMA * immediatly. * * Interrupts: * ----------- * For each DMA transfer, we will receive two interrupts: One when 1st * half the buffer is transfered, one when 2nd half of the buffer is * transfered. We must fill the half that has just been transfered with * fresh data. Note that in Stereo mode, there are two DMA operation * going. So when we receive Interrupt for one DMA, we must wait for the * exact interrupt from the other DMA and service both DMA's half buffers. * * Card Address and IRQ * -------------------- * We will use the default bus address of 0x330 and IRQ 5. Change in * bus address should also be reflected in /var/sysgen/system/rap.sm * file. Changes in IRQ should be reflected in the source code and * the program must be recomplied. * * ISSUES: * ------- * 1. The DMA processing and transfer of data from/to user's buffer * are independent of each other. When we are servicing the * one half of the dma buffer that just been transfered, there is * no guarantee that we can fill that half of the buffer BEFORE * dma is done with the other half. In this case, dma plays the * fist half of buffer WHILE we are writing into it. * * 2. Currently eisa_dma_disable() routine does not actually * releases the Dma channels. This is the reason why we access * the Dma channel table (e_ch[]) ourselves and release the * channel. * * 3. Somehow because of number 2, the Play program cannot be * stopped with a Ctrl-C. In Play program this signal is * explicitly ignored. Trapping a Ctrl-C causes a kernel panic. * Once we have a workable eisa_dma_disable(), this problem will * be resolved. * * TECHNICAL REFERENCES: * --------------------- * Roland RAP-10 Technical Reference and Programmer's Guide, Ver. 1.1 * IRIX Device Driver Programming Guide * IRIX Device Driver Reference Pages. * Intel 82357 Preliminary Reference, Section: 3.7.8 Mode Register (pp: 223) * ****************************************************************************** *** *** *** Copyright 1994, Silicon Graphics Inc., Mountain View, CA. *** *** *** ****************************************************************************** */ #include "sys/types.h" #include "sys/file.h" #include "sys/errno.h" #include "sys/open.h" #include "sys/conf.h" #include "sys/cmn_err.h" #include "sys/debug.h" #include "sys/param.h" #include "sys/edt.h" #include "sys/pio.h" #include "sys/uio.h" #include "sys/proc.h" #include "sys/user.h" #include "sys/eisa.h" #include "sys/sema.h" #include "sys/buf.h" #include "sys/cred.h" #include "sys/kmem.h" #include "sys/ddi.h" #include "./rap.h" /* * Macros to Read/Write 8 and 16-bit values from an address */ #define OUTB(addr, b) ( *(volatile uchar_t *)(addr) = (b) ) #define INPB(addr) ( *(volatile uchar_t *)(addr) ) #define OUTW(addr, w) ( *(volatile ushort_t *)(addr) = (w) ) #define INPW(addr) ( *(volatile ushort_t *)(addr) ) /* * Raising and lowering CPU interrupt */ #define LOCK() spl5() #define UNLOCK(s) splx(s) #define FROM_INTR 1 #define FROM_USR 0 #define User_pid u.u_procp->p_pid /* * IRQ and DMA channels we need. * */ #define IRQ_MASK 0x0020 #define DMAC_CH5 0x20 /* DMA Channel 5 */ #define DMAC_CH6 0x40 /* DMA Channel 6 */ /*=======================================* * MIDI and RAP Registers * *=======================================* * * The following is a description of RAP-10 registers. The same * names used throughout this program. Some of these registers are * 8-bit and some are 16-bit long. * * mdrd: MIDI Receive Data * mdtd: MIDI Transmit Data * mdst: MIDI Status * mdcm: MIDI Command * pwmd: Pulse Width Modulation Data * timm: Timer MSB data * gpcm: GPCC Command * dtci: DMA Transfer Count Buffer Interrupt Status * adcm: GPCC Analog to Digital Command * dacm: D/A Command and DMA Transfer Configuration * gpis: GPCC Interrupt Status * gpdi: GPCC DMA/Interrupt Enable * gpst: GPCC Status * dad0: Digital to Analog Data Channel 0 * addt: A/D Data Transfer * dad1: Digital to Analog Data Channel 1 * timd: Timer Data * cmp0: Compare Register Channel 0 * dtcd: DMA Transfer Count Data * cmp1: Compare Register Channel 1 * * These defines indicate the offsets of the above registers * from the Drive's base address: */ #define MDRD 0x0 #define MDTD 0x0 #define MDST 0x1 #define MDCM 0x1 #define PWMD 0x2 #define TIMM 0x3 #define GPCM 0x3 #define DTCI 0x4 #define ADCM 0x4 #define DACM 0x5 #define GPIS 0x6 #define GPDI 0x6 #define GPST 0x8 #define DAD0 0x8 #define ADDT 0xa #define DAD1 0xa #define TIMD 0xc #define CMP0 0xc #define DTCD 0xe #define CMP1 0xe typedef struct rapReg { uchar_t mdrd; uchar_t mdtd; uchar_t mdst; uchar_t mdcm; uchar_t pwmd; uchar_t timm; uchar_t gpcm; uchar_t dtci; uchar_t adcm; uchar_t dacm; ushort_t gpis; ushort_t gpdi; ushort_t gpst; ushort_t dad0; ushort_t addt; ushort_t dad1; ushort_t timd; ushort_t cmp0; ushort_t dtcd; ushort_t cmp1; } rapReg_t; /*==========================================================* * dtct (DMA Transfer Count) * *==========================================================*/ #define DTCD_DRQ0 0x00FF /* DRQ 0 bits (0-7) */ #define DTCD_DRQ1 0xFF00 /* DRQ 1 bits (8-15) */ /*==========================================================* * gpst (GPCC Status) * *==========================================================*/ #define GPST_PWM2 0x0800 /* PWM2 Busy (0=Write Enable, 1=Busy) */ #define GPST_PWM1 0x0400 /* PWM1 Busy (0=Write Enable, 1=Busy) */ #define GPST_PWM0 0x0200 /* PWM0 Busy (0=Write Enable, 1=Busy) */ #define GPST_EPB 0x0100 /* EP Convertor Busy (0=Write Enable, 1=Busy) */ #define GPST_GP1 0x0080 /* GP-chip, Ch 1 Acess (1 = Access) */ #define GPST_GP0 0x0040 /* GP-chip, Ch 0 Acess (1 = Access) */ #define GPST_MTE 0x0020 /* MIDI Tx Enable (0=Tx_Fifo buff full) */ #define GPST_ORE 0x0010 /* MIDI Overrun Error (1 = error) */ #define GPST_FE 0x0008 /* MIDI Framing Error (1 = error) */ #define GPST_ADE 0x0004 /* A/D Error (1 = error) */ #define GPST_DE1 0x0002 /* D/A Ch 1 Write Error (1 = error) */ #define GPST_DE0 0x0001 /* D/A Ch 0 Write Error (1 = error) */ /*==========================================================* * gpdi (GPCC DMA/Interrupt Enable (pp: 4-18) * *==========================================================*/ #define GPDI_ITC 0x8000 /* DMA Transfer Cnt Match (0=Disable) */ #define GPDI_DC2 0x4000 /* DMA Chann. Assignment, bit2 (pp:4-18) */ #define GPDI_DC1 0x2000 /* DMA Chann. Assignment, bit1 (pp:4-18) */ #define GPDI_DC0 0x1000 /* DMA Chann. Assignment, bit0 (pp:4-18) */ #define GPDI_DT1 0x0800 /* DMA Trans. Mode, bit:1 (pp: 4-18) */ #define GPDI_DT0 0x0400 /* DMA Trans. Mode, bit:0 (pp: 4-18) */ #define GPDI_OVF 0x0200 /* Free Run.Cntr (FCR) Ov.Flow (0=Disable)*/ #define GPDI_TC1 0x0100 /* Timer 1 Compare Match (0=Disable) */ #define GPDI_TC0 0x0080 /* Timer 0 Compare Match (0=Disable) */ #define GPDI_RXD 0x0040 /* MIDI Data Read Request (0=Disable) */ #define GPDI_TXD 0x0020 /* MIDI Tx_fifo Buf Empty (0=Disable) */ #define GPDI_ADD 0x0010 /* A/D Data Ready (0=Disable) */ #define GPDI_DN1 0x0008 /* D/A Ch1 Note ON Ready (0=Disable) */ #define GPDI_DN0 0x0004 /* D/A Ch0 Note ON Ready (0=Disable) */ #define GPDI_DQ1 0x0002 /* D/A Ch1 Data Request (0=Disable) */ #define GPDI_DQ0 0x0001 /* D/A Ch0 Data Request (0=Disable) */ /*==========================================================* * gpis (GPCC Interrupt Status .. pp: 4-16) * *==========================================================*/ #define GPIS_ITC 0x8000 /* DMA Transfer Count Match */ #define GPIS_JSD 0x0400 /* Joystick Data Ready */ #define GPIS_OVF 0x0200 /* Free Running Countr Overflow */ #define GPIS_TC1 0x0100 /* Timer1 Compare Match */ #define GPIS_TC0 0x0080 /* Timer0 Compare Match */ #define GPIS_RXD 0x0040 /* MIDI Data Read Request */ #define GPIS_TXD 0x0020 /* MIDI Tx_fifo Buf. Empty */ #define GPIS_ADD 0x0010 /* A/D Data Ready */ #define GPIS_DN1 0x0008 /* D/A Ch1 Note ON Ready */ #define GPIS_DN0 0x0004 /* D/A Ch0 Note ON Ready */ #define GPIS_DQ1 0x0002 /* D/A Ch1 Data Request */ #define GPIS_DQ0 0x0001 /* D/A Ch0 Data Request */ /*===================================================================* * dacm (Digital To Analogue Cmd and DMA Transfer Config) * *===================================================================*/ #define DACM_SCC 0x80 /* DMA Size Cmp. Cnt (0=in Sample, 1=in Bytes)*/ #define DACM_TS2 0x40 /* DMA Trnsfr Size, bit 2 (pp: 4-14) */ #define DACM_TS1 0x20 /* DMA Trnsfr Size, bit 1 (pp: 4-14) */ #define DACM_TS0 0x10 /* DMA Trnsfr Size, bit 0 (pp: 4-14) */ #define DACM_DL1 0x08 /* Ch1 DA Data Len (0=8 bit, 1=17 bit) */ #define DACM_DL0 0x04 /* Ch0 DA Data Len (0=8 bit, 1=17 bit) */ #define DACM_DS1 0x02 /* Ch1 DA Convrsion (0=Stop, 1=Start) */ #define DACM_DS0 0x01 /* Ch0 DA Convrsion (0=Stop, 1=Start) */ /*=====================================================* * adcm ( GPCC AD Command ) * *=====================================================*/ #define ADCM_MON 0x40 /* Monitor MIC (0=Monitor Off) */ #define ADCM_GIN 0x20 /* Gain Input (0=Line, 1=Mic) */ #define ADCM_AF1 0x10 /* Analog Freq Selection bit 1 (pp: 4-13) */ #define ADCM_AF0 0x08 /* Analog Freq Selection bit 0 (pp: 4-13) */ #define ADCM_ADL 0x04 /* Analog Data Length (0=8, 1=16) */ #define ADCM_ADM 0x02 /* Analog Data Conv. Mode (0=Mono,1=Stereo) */ #define ADCM_ADS 0x01 /* Analog Data Conv. Start(0=Stop,1=Start) */ /*=====================================================* * dtci ( DMA Trans.Count Buf Intr. Stat * *=====================================================*/ #define DTCI_BF1 0x08 /* DMA DRQ1 buff full (1 = full) */ #define DTCI_BH1 0x04 /* DMA DRQ1 buff half (1 = full) */ #define DTCI_BF0 0x02 /* DMA DRQ0 buff full (1 = full) */ #define DTCI_BH0 0x01 /* DMA DRQ0 buff half (1 = full) */ /*========================================* * gpcm ( GPCC Command ) * *========================================*/ #define GPCM_RST 0x80 /* Reset bit */ #define GPCM_PWM2 0x10 /* Select PWM channel 2 */ #define GPCM_PWM1 0x08 /* Select PWM channel 1 */ #define GPCM_PWM0 0x04 /* Select PWM channel 0 */ #define GPCM_FRCM 0x02 /* Free Run. Counter (1=Start) */ #define GPCM_MTT 0x01 /* MIDI Timed Trans */ /* ( 1 = Timer INT enabled ) */ /*======================================* * timm (Timer MSB data) * *======================================*/ #define TIMM_FRC 0x04 /* Free Running Counter Bit 16 */ #define TIMM_CR1 0x02 /* Compare Reg 1 Bit 16 */ #define TIMM_CR0 0x01 /* Compare Reg 0 Bit 16 */ /*===================================* * mdcm (MIDI Command) * *===================================*/ #define MDCM_UART 0x3f /* UART mode */ #define MDCM_MPU 0xff /* MPU Reset */ #define MDCM_VERSION 0xac /* Version */ #define MDCM_REVISION 0xad /* Revision */ /*===================================* * mdst (MIDI Status) * *===================================*/ #define MDST_DSR 0x80 /* DSR = 0 if ready */ #define MDST_DDR 0x40 /* DDR = 0 if ready */ /*====================================* * RAP Card Info * *====================================* * * These are the information regarding the RAP Card. * The info being tracked are: * * ci_state: Our state (Installed, Opened, Playing, Recording) * ci_pid: PID of process opened us. * ci_addr[]: EISA Addresses * ci_irq: EISA Interrupt number we use * ci_ctl: Controller number we save from edt struct * ci_adap: Adaptor number we save from edt struct. * ci_dmaCh6: DMA Channel 6 * ci_dmaCh5: DMA Channel 5 * ci_dmaBuf6: EISA DMA Buffer struct for Channel 6 * ci_dmaBuf5: EISA DMA Buffer struct for Channel 5 * ci_dmaCb6: EISA DMA Control Block for Channel 6 * ci_dmaCb5: EISA DMA Control Block for Channel 5 * di_state: DMA buffers state (Idle, Progress) * di_idx: Current rwQue[] entry being used. * di_ptr: Address in rwQue buffer * di_which: Which half of DMA buffer (0=1st half, 1=2nd Half) * di_bh: Total DMA Buffer Half (BH) Interrupt received. * di_bf: Total DMA Buffer Full (BF) Interrupt received. * ri_state: State of Circular buffer (Wanted_Empty, etc.) * ri_free: Total Free entries in rwQue[] * ri_full: Total Full entries in rwQue[] * ri_idx: Current rwBuf for Read/Write * ri_tout; =1 if Timed out on read/write * ri_note; number of Note_On received * ri_ptr: Pointer in current rwBuf */ typedef struct eisa_dma_buf dmaBuf_t; typedef struct eisa_dma_cb dmaCb_t; typedef struct cardInfo_s { /* Card Installation Info */ ushort_t ci_state; pid_t ci_pid; caddr_t ci_addr[NBASE]; int ci_irq; int ci_ctl; int ci_adap; int ci_dmaCh6; int ci_dmaCh5; dmaBuf_t *ci_dmaBuf6; dmaBuf_t *ci_dmaBuf5; dmaCb_t *ci_dmaCb6; dmaCb_t *ci_dmaCb5; /* DMA Buffer Information data */ uchar_t di_state; short di_idx; uchar_t di_which; caddr_t di_ptr; uchar_t di_bh; uchar_t di_bf; /* Circular buffer Information data */ uchar_t ri_state; short ri_free; short ri_full; short ri_idx; uchar_t ri_tout; uchar_t ri_note; caddr_t ri_ptr; } cardInfo_t; /* ci_state values */ #define CARD_INSTALLED 0x0001 #define CARD_STEREO 0x0002 #define CARD_OPENED 0x0004 #define CARD_PLAYING 0x0010 #define CARD_RECORDING 0x0020 /* di_state values */ #define DI_DMA_IDLE 0x00 #define DI_DMA_PLAYING 0x01 #define DI_DMA_RECORDING 0x02 #define DI_DMA_END_PLAY 0x04 #define DI_DMA_END_RECORD 0x08 /* ri_state values */ #define RI_WANTED_EMPTY 0x01 /*====================================* * Read/Write Circular Buffers * *====================================* * This is the description of our circular buffers used * to store D/A and A/D values. D/A values are stored from * user's buffer and then moved to DMA buffers. A/D data is * moved from DMA buffers to these buffers and then moved * to user's buffer. The fields are as follow: * rw_state: buffer state (Empty, Busy, Full) * rw_idx: Index of this buffer in rwQue[]; * rw_count: Total bytes in the buffer * rw_buf[]: The buffer itself. * RW_MIN_FULL: We will start a D/A DMA when we have this many * full buffer on hand. This is done so that we can * provide enough full buffers for DMA to process. */ #define RW_BUF_SIZE 8192 #define RW_BUF_COUNT 20 #define RW_MIN_FULL 1 #define RW_TIMEOUT 1600 typedef struct rwBuf_s { uchar_t rw_state; short rw_idx; int rw_count; uchar_t rw_buf[RW_BUF_SIZE]; } rwBuf_t; /* rw_state values */ #define RW_EMPTY 0x00 /* used as parameter only */ #define RW_FULL 0x01 #define RW_WANTED_FULL 0x02 #define RW_WANTED_EMPTY 0x04 /*==================================* * Global values * *==================================*/ #define DMA_BUF_SIZE 8192 #define DMA_HALF_SIZE 4096 int rapdevflag = 0; static cardInfo_t cardInfo; static caddr_t dmaRight; static caddr_t dmaLeft; static paddr_t dmaRightPhys; static paddr_t dmaLeftPhys; static rwBuf_t rwQue[RW_BUF_COUNT]; static caddr_t eisa_addr; /* * Eisam Dma Channel semaphores..shoule be removed when * proper way of releasing channels found */ extern struct eisa_ch_state { sema_t chan_sem; /* inuse semaphore for each channel */ sema_t dma_sem; /* dma completion semaphore */ struct eisa_dma_buf *cur_buf; /* current eisa_dma_buf being dma'ed */ struct eisa_dma_cb *cur_cb; /* ptr to current command block */ int count; } e_ch[]; /*=========================================* * Driver Entry routines Data * *=========================================*/ int rapopen ( dev_t *, int, int, cred_t * ); int rapread ( dev_t, uio_t *, cred_t * ); int rapwrite ( dev_t, uio_t *, cred_t * ); int rapclose ( dev_t, int, int, cred_t * ); void rapedtinit ( struct edt * ); void rapintr ( int ); int rapioctl (dev_t, int, void *, int, cred_t *, int *); /*=======================================* * Misc and Internal routines * *=======================================*/ static void rapDisInt (cardInfo_t *); static int rapGetDma( dmaBuf_t **, dmaCb_t **, int ); static int rapClose(uchar_t); static short rapGetNextEmpty (short, uchar_t); static short rapGetNextFull (short, uchar_t); static void rapPrepEisa( dmaBuf_t *, dmaCb_t *, uchar_t, paddr_t); static int rapStart(uchar_t); static void rapStop(uchar_t); static void rapStartDA(); static void rapStartAD(); static void rapBufToDma( int ); static void rapDmaToBuf( int ); static void rapMarkBuf(rwBuf_t *, cardInfo_t *, uchar_t); static int rapKernMem(uchar_t); static void rapSetAutoInit(cardInfo_t *, uchar_t); static void rapTimeOut( void *); static void rapNoteOn(cardInfo_t *, ushort_t ); static void rapNoteOff(cardInfo_t *); static void rapZeroDma(cardInfo_t *, int); static void rapReleaseDma (cardInfo_t *); /************************************************************************* * r a p e d t i n i t ************************************************************************* * Name: rapedtint * Purpose: Initializes the driver. Called once for each controller. * Called only once. * Returns: None. *************************************************************************/ void rapedtinit ( struct edt *e ) { int ctl, iospace, dmac, eirq; cardInfo_t *ci; piomap_t *pmap; iospace_t eisa_io; ci = &cardInfo; cmn_err (CE_NOTE, "rapedtinit: Installing RAP board."); bzero ((void *)ci, sizeof(cardInfo_t) ); dmaRight = dmaLeft = (caddr_t)NULL; ci->ci_ctl = e->e_ctlr; ci->ci_adap = e->e_adap; /* * Get the base address of Eisa bus (for rapSetAutoInit) */ bzero (&eisa_io, sizeof(iospace_t)); eisa_io.ios_iopaddr = 0; eisa_io.ios_size = 1000; pmap = pio_mapalloc (e->e_bus_type, 0, &eisa_io, PIOMAP_FIXED, "eisa"); if ( pmap == (piomap_t *)NULL ) { cmn_err (CE_WARN, "rapedtinit: Cannot get Eisa bus address"); return; } eisa_addr = pio_mapaddr (pmap, eisa_io.ios_iopaddr); #ifdef DEBUG cmn_err (CE_NOTE, "rapedtinit: Eisa base address = %x", eisa_addr); #endif /*===================================================* * map EISA IO/Memory addresses for RAP-10 card * *===================================================*/ for ( iospace = 0; iospace < NBASE; iospace++ ) { /* any address to map ? */ if ( !e->e_space[iospace].ios_iopaddr ) continue; pmap = pio_mapalloc ( e->e_bus_type, e->e_adap, &e->e_space[iospace], PIOMAP_FIXED, "rap10" ); ci->ci_addr[iospace] = pio_mapaddr ( pmap, e->e_space[iospace].ios_iopaddr ); } /* is Card still there ? */ if ( badaddr(ci->ci_addr[0], 1) ) { cmn_err (CE_WARN, "rapedtinit: RAP board not installed."); return; } #ifdef DEBUG cmn_err (CE_NOTE, "rapedtinit: First Load..allocating IRQ"); #endif eirq = eisa_ivec_alloc( e->e_adap, IRQ_MASK, EISA_EDGE_IRQ ); if ( eirq < 0 ) { cmn_err (CE_WARN, "rapedtinit: Could not allocate IRQ for RAP card."); return; } /* set Interrupt handler */ #ifdef DEBUG cmn_err (CE_NOTE, "rapedtinit: Setting Interrupt Handler for IRQ %d", eirq); #endif if ( eisa_ivec_set(e->e_adap, eirq, rapintr, e->e_ctlr) == -1 ) { cmn_err (CE_NOTE, "rapedtinit: Could not set Interrupt handler for Irq %d", eirq); ci->ci_state = 0; return; } ci->ci_irq = eirq; /*======================================* * DMA Channels Allocation * *======================================*/ /* DMA channel 5 */ dmac = eisa_dmachan_alloc ( e->e_adap, DMAC_CH5 ); if ( dmac < 0 ) { cmn_err (CE_WARN, "rapedtinit: Could not allocate DMA Channel 5."); return; } ci->ci_dmaCh5 = dmac; /* DMA channel 6 */ dmac = eisa_dmachan_alloc ( e->e_adap, DMAC_CH6 ); if ( dmac < 0 ) { cmn_err (CE_WARN, "rapedtinit: Could not allocate DMA Chann 6."); cmn_err (CE_WARN, "rapedtinit: RAP is initialized as Mono."); } else { ci->ci_dmaCh6 = dmac; ci->ci_state |= CARD_STEREO; } /*==============================* * DMA Buffer allocation * *==============================*/ if ( rapKernMem (1) ) { cmn_err (CE_WARN, "rapedtinit: Did not install RAP-10."); return; } ci->ci_state |= CARD_INSTALLED; #ifdef DEBUG cmn_err (CE_NOTE, "rapedtinit: RAP installed, Addr: %x, Irq: %d.", ci->ci_addr[0], ci->ci_irq ); cmn_err (CE_NOTE, "rapedtinit: Init as %s, Dma 1 = %d, Dma 0 = %d", (ci->ci_state & CARD_STEREO ? "Stereo":"Mono"), ci->ci_dmaCh5, ci->ci_dmaCh6); #endif return; } /*** End rapedtinit ***/ /************************************************************************* * r a p o p e n ************************************************************************* * Name: rapopen * Purpose: Opens the RAP board and initializes necessary data * Returns: 0 = Success, or appropriate error number. *************************************************************************/ int rapopen ( dev_t *dev, int oflag, int otyp, cred_t *cred) { register int i; cardInfo_t *ci; rwBuf_t *rw; dmaBuf_t *dmaB; dmaCb_t *dmaC; ci = &cardInfo; #ifdef DEBUG cmn_err (CE_NOTE, "rapopen: Opening, Addr = %x, ci_state = %x", ci->ci_addr[0], ci->ci_state ); #endif /* * No card is installed or card is already opened */ if ( !(ci->ci_state & CARD_INSTALLED) ) return (ENODEV); if ( ci->ci_state & CARD_OPENED ) return (EBUSY); /* Allocate DMA Buf and Cb for Channel 5 */ if ( ci->ci_dmaBuf5 == (dmaBuf_t *)NULL ) { if ( rapGetDma(&dmaB, &dmaC, ci->ci_dmaCh5) ) { cmn_err (CE_WARN,"rapopen: Could not allocate DMA Buf 5."); return (ENOMEM); } ci->ci_dmaBuf5 = dmaB; ci->ci_dmaCb5 = dmaC; } /* if in stereo, do the same for Channel 6 */ if ( ci->ci_state & CARD_STEREO ) { if ( rapGetDma(&dmaB, &dmaC, ci->ci_dmaCh6) ) { cmn_err (CE_WARN, "rapopen: Could not allocate DMA Buf 6."); return (ENOMEM); } ci->ci_dmaBuf6 = dmaB; ci->ci_dmaCb6 = dmaC; } /* Initialize Card Info structure */ ci->ri_idx = 0; ci->di_idx = 0; ci->ri_state = 0; ci->di_state = 0; ci->di_ptr = 0; ci->ri_ptr = 0; ci->ri_free = RW_BUF_COUNT; ci->ri_full = 0; ci->ci_state &= ~(CARD_PLAYING | CARD_RECORDING ); ci->ci_state |= CARD_OPENED; ci->ci_pid = User_pid; /* Initialize Circular Buffers */ for ( i = 0; i < RW_BUF_COUNT; i++ ) { rw = &rwQue[i]; rw->rw_count = 0; rw->rw_state = 0; rw->rw_idx = i; bzero (rw->rw_buf, RW_BUF_SIZE); } rapDisInt(ci); #ifdef DEBUG cmn_err (CE_NOTE, "rapopen: Opened succesfully"); #endif return(0); } /*** End rapopen ***/ /************************************************************************* * r a p w r i t e ************************************************************************* * Name: rapwrite * Purpose: Write entry routine. This routine will transfer user's * data to current or an empty entry in rwQue[] and starts * DMA if none is going. * Returns: 0 = Success, or errno *************************************************************************/ int rapwrite (dev_t dev, uio_t *uio, cred_t *cred) { cardInfo_t *ci; rwBuf_t *rw; toid_t to_id; int avail, size, totBytes, err, s; ci = &cardInfo; /*=========================* * Error Checking * *=========================*/ /* no card is installed */ if ( !(ci->ci_state & CARD_INSTALLED) ) return (ENODEV); /* card is not opened */ if ( !(ci->ci_state & CARD_OPENED) ) return (EACCES); /* we are not the owner */ if ( ci->ci_pid != User_pid ) return (EACCES); /* is busy recording */ if ( ci->ci_state & CARD_RECORDING ) return (EACCES); ci->ci_state |= CARD_PLAYING; rw = &rwQue[ci->ri_idx]; #ifdef DEBUG cmn_err (CE_NOTE, "rapwrite: %d bytes, buf = %d, rw_count = %d, free = %d, full = %d", uio->uio_resid, ci->ri_idx, rw->rw_count, ci->ri_free, ci->ri_full); #endif /* if it is full, wait till it is Empty */ s = LOCK(); if ( rw->rw_state & RW_FULL ) { ci->ri_ptr = NULL; ci->ri_tout = 0; to_id = itimeout (rapTimeOut, rw, RW_TIMEOUT, plbase, 0, 0, 0); while ( (rw->rw_state & RW_FULL) && !ci->ri_tout ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapwrite: waiting for buf %d to be Empty", rw->rw_idx ); #endif rw->rw_state |= RW_WANTED_EMPTY; if ( sleep (rw, PUSER | PCATCH) ) { untimeout(to_id); #ifdef DEBUG cmn_err (CE_NOTE, "rapwrite: Interrupted"); #endif rw->rw_state &= ~RW_WANTED_EMPTY; UNLOCK(s); return (EINTR); } } /* while */ untimeout(to_id); /* we timed out ..couldn't get the buffer */ if ( ci->ri_tout ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapwrite: Timed out"); #endif rw->rw_state &= ~RW_WANTED_EMPTY; UNLOCK(s); return (EIO); } } /* if (rw->rw_state & RW_FULL */ UNLOCK(s); /* adjuest the read/write address if necessary */ if ( ci->ri_ptr == NULL ) ci->ri_ptr = rw->rw_buf; totBytes = uio->uio_resid; while ( totBytes > 0 ) { avail = RW_BUF_SIZE - rw->rw_count; /* if this buffer is full, get next buffer */ if ( avail <= 0 ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapwrite: Buffer %d is Full now, rw_count = %d", rw->rw_idx, rw->rw_count); #endif s = LOCK(); rapMarkBuf(rw, ci, RW_FULL); /* wake anyone wanted this buffer full */ if ( rw->rw_state & RW_WANTED_FULL ) { #ifdef DEBUG cmn_err (CE_NOTE,"rapwrite: Buffer %d is Wanted_Full", rw->rw_idx ); #endif rw->rw_state &= ~RW_WANTED_FULL; wakeup(rw); } /* * start DMA if none is going and we filled the * entire buffers. */ if ( (ci->di_state == DI_DMA_IDLE) && (rw->rw_idx >= RW_MIN_FULL ) ) { #ifdef DEBUG cmn_err (CE_NOTE,"rapwrite: Starting Play Dma"); #endif err = rapStart(DI_DMA_PLAYING); if ( err ) { cmn_err (CE_WARN, "rapwrite: Could not start playing error %d",err ); UNLOCK(s); return(err); } } /* get next empty buffer */ ci->ri_idx = rapGetNextEmpty(ci->ri_idx, FROM_USR); rw = &rwQue[ci->ri_idx]; ci->ri_ptr = rw->rw_buf; UNLOCK(s); continue; } /* start filling this buffer */ size = (totBytes > avail ? avail: totBytes); err = uiomove (ci->ri_ptr, size, UIO_WRITE, uio); if ( err ) { cmn_err (CE_NOTE, "rapwrite: uiomov error %d", err); return(err); } rw->rw_count += size; ci->ri_ptr += size; totBytes = uio->uio_resid; #ifdef DEBUG cmn_err (CE_NOTE, "rapwrite: Wrote %d to Buffer %d, Left = %d, rw_count = %d", size, rw->rw_idx, totBytes, rw->rw_count ); #endif } return (0); } /*** end rapwrite ***/ /************************************************************************* * r a p r e a d ************************************************************************* * * Name: rapread * * Purpose: Reads data from rwQue[] into user's buffer. * This routine waits for current DMA operation to end * and then starts a A/D Dma (recording). If A/D is already * going then it simply moves data from current Full buffer * into user's buffer. If buffer is not full, it waits for * it to get full. * * Returns: 0 = Success, or errno. * *************************************************************************/ int rapread (dev_t dev, uio_t *uio, cred_t *cred) { cardInfo_t *ci; rwBuf_t *rw; toid_t to_id; int avail, size, totBytes, err, s; ci = &cardInfo; /*===============================* * Error Checking * *===============================*/ /* card is not installed */ if ( !(ci->ci_state & CARD_INSTALLED) ) return (ENODEV); /* card is not opened */ if ( !(ci->ci_state & CARD_OPENED) ) return (EACCES); /* we do not own the card */ if ( ci->ci_pid != User_pid ) return (EACCES); /* card is in middle of a Play operation */ if ( ci->ci_state & CARD_PLAYING ) return (EIO); ci->ci_state |= CARD_RECORDING; /* start a A/D Dma if none is going on */ if ( ci->di_state == DI_DMA_IDLE ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapread: Idle DMA. Starting one"); #endif if ( rapStart(DI_DMA_RECORDING) ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapread: Could not start A/D"); #endif ci->ci_state &= ~CARD_RECORDING; UNLOCK(s); return (EIO); } } /* * get the buffer we should be using and * wait for it to become Full */ rw = &rwQue[ci->ri_idx]; #ifdef DEBUG cmn_err (CE_NOTE, "rapread: %d bytes, buf = %d, rw_count = %d, free = %d, full = %d", uio->uio_resid, ci->ri_idx, rw->rw_count, ci->ri_free, ci->ri_full); #endif s = LOCK(); if ( !(rw->rw_state & RW_FULL) ) { ci->ri_ptr = NULL; ci->ri_tout = 0; to_id = itimeout (rapTimeOut, rw, RW_TIMEOUT, plbase, 0, 0, 0); while ( !(rw->rw_state & RW_FULL) && !ci->ri_tout ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapread: wating for buf %d to become Full", rw->rw_idx ); #endif rw->rw_state |= RW_WANTED_FULL; if ( sleep (rw, PUSER | PCATCH) ) { untimeout (to_id); #ifdef DEBUG cmn_err (CE_NOTE, "rapread: Interrupted"); #endif rw->rw_state &= ~RW_WANTED_FULL; UNLOCK(s); return(EINTR); } } /* while */ untimeout (to_id); if ( ci->ri_tout ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapread: Timed out"); #endif rw->rw_state &= ~RW_WANTED_FULL; UNLOCK(s); return (EIO); } } /* if !rw->rw_state & RW_FULL */ UNLOCK(s); /* adjust read/write pointer if necessary */ if ( ci->ri_ptr == NULL ) ci->ri_ptr = rw->rw_buf; /*===================================* * Actual Read (Data movement) * *===================================*/ totBytes = uio->uio_resid; while ( totBytes > 0 ) { avail = rw->rw_count; /* if this buffer is Empty, get next Full buffer */ if ( avail <= 0 ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapread: Buffer %d is Empty now, rw_count = %d", rw->rw_idx, rw->rw_count ); #endif s = LOCK(); rapMarkBuf(rw, ci, RW_EMPTY); /* wake anyone wanted this buffer Empty */ if ( rw->rw_state & RW_WANTED_EMPTY ) { #ifdef DEBUG cmn_err (CE_NOTE,"rapread: Buffer %d is Wanted_Empty", rw->rw_idx ); #endif rw->rw_state &= ~RW_WANTED_FULL; wakeup(rw); } /* get next Full buffer */ ci->ri_idx = rapGetNextFull(ci->ri_idx, FROM_USR); rw = &rwQue[ci->ri_idx]; ci->ri_ptr = rw->rw_buf; UNLOCK(s); continue; } /* start filling this buffer */ size = (totBytes > avail ? avail: totBytes); err = uiomove (ci->ri_ptr, size, UIO_READ, uio); if ( err ) { cmn_err (CE_PANIC, "rapread: uiomov error %d", err); return(err); } rw->rw_count -= size; ci->ri_ptr += size; totBytes = uio->uio_resid; #ifdef DEBUG cmn_err (CE_NOTE, "rapread: Read %d, Buffer %d, Left = %d, rw_count = %d", size, rw->rw_idx, totBytes, rw->rw_count ); #endif } return (0); } /*** End rapread ***/ /************************************************************************* * r a p c l o s e ************************************************************************* * Name: rapclose * Purpose: closes connection to the card and makes it available * for next process to open it. * Returns: 0 = Success, or errno *************************************************************************/ int rapclose (dev_t dev, int flag, int otyp, cred_t *cred) { cardInfo_t *ci; ci = &cardInfo; #ifdef DEBUG cmn_err (CE_NOTE, "rapclose: ci_state = %x, di_state = %x, full = %d, empty = %d", ci->ci_state, ci->di_state, ci->ri_full, ci->ri_free ); #endif /*=========================* * Error Checking * *=========================*/ /* card is not installed */ if ( !(ci->ci_state & CARD_INSTALLED) ) return (ENODEV); /* card is not opened */ if ( !(ci->ci_state & CARD_OPENED) ) return (EACCES); /* we do not own the card */ if ( ci->ci_pid != User_pid ) return (EACCES); return ( rapClose(1) ); } /************************************************************************* * r a p i n t r ************************************************************************* * Name: rapintr * Purpose: Interrupt handling routine * Returns: None. *************************************************************************/ void rapintr ( int ctl ) { ushort_t gpis; uchar_t dtci; uchar_t stereo; uchar_t totreq; uchar_t playing; uchar_t moveData; cardInfo_t *ci; caddr_t addr; ci = &cardInfo; addr = ci->ci_addr[0]; /* * moveData: 0 = we should move data between Buf/DMA to DMA/Buf. * totreq: In stereo, we have to wait for 2 BF or BH interrupt * but in Mono we have to wait for only one. * playing: 1 = Playing, 0= Recording. */ moveData = 0; totreq = (ci->ci_state & CARD_STEREO? 2:1); /* No. of Ints. we need */ playing = ci->ci_state & CARD_PLAYING; gpis = INPW(addr+GPIS); /* * First, check for stray interrupts and ignore them */ if ( !(ci->ci_state & (CARD_PLAYING | CARD_RECORDING)) ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapintr: Stray interupt, gpis = %x, ci_state = %x", ci->ci_state ); #endif return; } #ifdef DEBUG cmn_err (CE_NOTE, "rapintr: New ..Gpis = %x", gpis ); #endif /********************************** * DMA Buffers Half/Full * **********************************/ while ( gpis & GPIS_ITC ) { /* see which buffer is half/full */ dtci = INPB(addr+DTCI); #ifdef DEBUG cmn_err (CE_NOTE, "rapintr: Dma buffer status..Gpis = %x, Dtci = %x", gpis, dtci); #endif if ( dtci & DTCI_BF0 ) ci->di_bf++; if ( dtci & DTCI_BF1 ) ci->di_bf++; if (dtci & DTCI_BH0 ) ci->di_bh++; if (dtci & DTCI_BH1 ) ci->di_bh++; #ifdef DEBUG cmn_err (CE_NOTE, "rapintr: di_bf = %d, di_bh = %d", ci->di_bf, ci->di_bh ); #endif /* 1st half of dma needs service */ if ( ci->di_bh == totreq ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapintr: DMA First_Half needs service"); #endif ci->di_bh = 0; ci->di_which = 0; /* 1st half of DMA buffer */ moveData = 1; } /* 2nd half of dma needs service */ else if ( ci->di_bf == totreq ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapintr: DMA Second_Half needs service"); #endif ci->di_bf = 0; ci->di_which = 1; /* 2nd half of DMA buffer */ moveData = 1; } /* * Move data if needed */ if ( moveData ) { /* move data for Play if only data available */ if ( playing ) { /* No more data..end of play */ if ( ci->ri_full <= 0 ) { if (ci->di_state & DI_DMA_END_PLAY ) { #ifdef DEBUG cmn_err (CE_NOTE,"rapintr: End of Play Reached"); #endif if ( ci->ri_state & RI_WANTED_EMPTY ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapintr: Cir.Buff is Wanted Empty"); #endif ci->ri_state &= ~RI_WANTED_EMPTY; wakeup (ci); } else rapStop(DI_DMA_PLAYING); return; } else { #ifdef DEBUG cmn_err (CE_NOTE, "rapintr: Playing but no Full buffers"); #endif return; } } /* Data is available to play */ #ifdef DEBUG cmn_err (CE_NOTE, "rapintr: Playing..which = %d, idx = %d, full = %d, Empty = %d", ci->di_which, ci->di_idx, ci->ri_full, ci->ri_free); #endif rapBufToDma(DMA_HALF_SIZE); } /* if playing */ else { /* recording */ #ifdef DEBUG cmn_err (CE_NOTE, "rapintr: Recording..which = %d, full = %d, Empty = %d", ci->di_which, ci->ri_full, ci->ri_free); #endif rapDmaToBuf(DMA_HALF_SIZE); } } /* if move data */ else { /* no need to move data */ #ifdef DEBUG cmn_err (CE_NOTE, "rapintr: Waiting for next interrupt, bf = %d, bh = %d", ci->di_bf, ci->di_bh); #endif } gpis = INPW(addr+GPIS); #ifdef DEBUG cmn_err (CE_NOTE, "rapintr: next Gpis = %x", gpis); #endif } /* while ( gpis & .. */ #ifdef DEBUG cmn_err (CE_NOTE, "rapintr: finished ..."); #endif } /*** End rapintr ***/ /************************************************************************* * r a p i o c t l ************************************************************************* * Name: rapioctl * Purpose: handles IOCTL calls for RAP-10. * Returns: 0 = Success, or errno *************************************************************************/ int rapioctl (dev_t dev, int cmd, void *arg, int mode, cred_t *cred, int *ret) { cardInfo_t *ci; ci = &cardInfo; #ifdef DEBUG cmn_err (CE_NOTE, "rapioctl: Cmd = %d, full = %d, Empty = %d", cmd, ci->ri_full, ci->ri_free ); #endif /* * No card is installed or card is already opened */ if ( !(ci->ci_state & CARD_INSTALLED) ) return (ENODEV); if ( !(ci->ci_state & CARD_OPENED) ) return (EACCES); if ( ci->ci_pid != User_pid ) return (EACCES); *ret = 0; switch ( cmd ) { case RAPIOCTL_END_PLAY: /*=======================* * End PLAY * *=======================*/ if ( !(ci->ci_state & CARD_PLAYING) ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapioctl: End_PLay command in wrong state"); #endif return (EACCES); } return (rapClose (0) ); case RAPIOCTL_END_RECORD: /*=======================* * End RECORD * *=======================*/ if ( !(ci->ci_state & CARD_RECORDING) ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapioctl: End_Recrd command in wrong state"); #endif return (EACCES); } return (rapClose (0) ); } /* switch */ return (0); } /** End rapioctl **/ /**************************************************************************** ****** I n t e r n a l R o u t i n e s ******* ****************************************************************************/ /************************************************************************* * r a p C l o s e ************************************************************************* * Name: rapClose * Purpose: Routine to actually ends current operation and releases * the card. It is written as a separate routine here so * it can be shared by rapclose() and rapioctl() routines. * One frees up the card, one does not. Also if we are called * from ioctl, we will wait till all buffers are played (if * in Playback mode). * Returns: 0 = Success, or errno *************************************************************************/ int rapClose( uchar_t relCard ) { cardInfo_t *ci; rwBuf_t *rw; int s, totLeft; ci = &cardInfo; s = LOCK(); rw = &rwQue[ci->ri_idx]; #ifdef DEBUG cmn_err (CE_NOTE, "rapClose: relCard = %d, ci_state = %x, di_state = %x", relCard, ci->ci_state, ci->di_state ); #endif /* * if we are not recording and are not playing * then simply mark the card as not opened and return */ if ( !(ci->ci_state & (CARD_RECORDING | CARD_PLAYING)) ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapClose: Idle card ..closing"); #endif if ( relCard ) { ci->ci_state &= ~CARD_OPENED; ci->ci_pid = 0; } UNLOCK(s); return(0); } /* * Recording ? end it. */ if ( ci->ci_state & CARD_RECORDING ) { #ifdef DEBUG cmn_err (CE_NOTE,"rapClose: Ending Record (A/D)"); #endif rapStop(DI_DMA_RECORDING); if ( relCard ) { ci->ci_state &= ~CARD_OPENED; ci->ci_pid = 0; } UNLOCK(s); return(0); } /* * playback and called from close() routine ? * End the playback */ if ( relCard ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapClose: Ending Playback (D/A"); #endif rapStop(DI_DMA_PLAYING); ci->ci_state &= ~CARD_OPENED; ci->ci_pid = 0; UNLOCK(s); return(0); } /* * Called from Ioctl. * Closing in middle of play is different based on we * have been called from close() routine or not. * If called from Ioctl (relCard = 0), we will wait till * all buffers are played back. */ if ( !(rw->rw_state & RW_FULL) && (rw->rw_count > 0) ) { totLeft = RW_BUF_SIZE - rw->rw_count; #ifdef DEBUG cmn_err (CE_NOTE, "rapClose: Current Buf %d has %d data. Filled with %d zeros", rw->rw_idx, rw->rw_count, totLeft ); #endif if ( totLeft > 0 ) { bzero (ci->ri_ptr, totLeft); ci->ri_ptr += totLeft; } rapMarkBuf(rw, ci, RW_FULL); } /* some buffers to play */ if ( ci->ri_full > 0 ) { /* Playback has not started yet */ if ( ci->di_state == DI_DMA_IDLE ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapClose: Starting playback, full = %d, empty = %d", ci->ri_full, ci->ri_free); #endif rapStart(DI_DMA_PLAYING); } ci->di_state = DI_DMA_IDLE; ci->di_state |= DI_DMA_END_PLAY; /* wait till buffers are all played back */ while ( ci->ri_full > 0 ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapClose: waiting for Play to end..full = %d, empty = %d, ri_idx = %d, di_idx = %d", ci->ri_full, ci->ri_free, ci->ri_idx, ci->di_idx); #endif ci->ri_state |= RI_WANTED_EMPTY; if ( sleep (ci, PUSER | PCATCH) ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapClose: Interrupted"); #endif rapStop(DI_DMA_PLAYING); ci->ci_state &= ~CARD_OPENED; ci->ci_pid = 0; UNLOCK(s); return (EINTR); } } rapStop(DI_DMA_PLAYING); } else { #ifdef DEBUG cmn_err (CE_NOTE, "rapClose: Circular buffer empty..closing"); #endif rapStop(DI_DMA_PLAYING); } UNLOCK(s); return(0); } /*** End rapClose ***/ /************************************************************************* * r a p S t o p ************************************************************************* * Name: rapStop * Purpose: Stops D/A and A/D conversion. * Returns: None. *************************************************************************/ static void rapStop( uchar_t what ) { cardInfo_t *ci; rwBuf_t *rw; caddr_t addr; uchar_t dacm, adcm; ushort_t gpdi; int s, i; s = LOCK(); ci = &cardInfo; addr = ci->ci_addr[0]; gpdi = adcm = dacm = 0; #ifdef DEBUG cmn_err (CE_NOTE, "rapStop: Stoping %s, full = %d, Empty = %d", (what == DI_DMA_PLAYING ? "Playback(D/A)":"Record(A/D)"), ci->ri_full, ci->ri_free); #endif switch ( what ) { /* stop D/A Conversion (Playing) */ case DI_DMA_PLAYING: ci->di_which = 0; rapZeroDma(ci, DMA_BUF_SIZE); OUTB(addr+DACM, dacm); rapNoteOff (ci); break; /* stop A/D Conversion (recording) */ case DI_DMA_RECORDING: OUTB(addr+ADCM, adcm); OUTB(addr+DACM, dacm); break; } OUTW(addr+GPDI, gpdi); rapReleaseDma(ci); /* Initialize Card Info structure */ ci->ci_state &= ~(CARD_PLAYING | CARD_RECORDING); ci->ri_idx = 0; ci->di_idx = 0; ci->ri_state = 0; ci->di_state = 0; ci->di_ptr = rwQue[0].rw_buf; ci->ri_ptr = rwQue[0].rw_buf; ci->ri_free = RW_BUF_COUNT; ci->ri_full = 0; /* Initialize Circular Buffers */ for ( i = 0; i < RW_BUF_COUNT; i++ ) { rw = &rwQue[i]; rw->rw_count = 0; rw->rw_state = 0; rw->rw_idx = i; bzero (rw->rw_buf, RW_BUF_SIZE); } /* clear out any hanging GPIS and DACM */ gpdi = INPW(addr+GPIS); UNLOCK(s); } /** End rapStop **/ /************************************************************************* * r a p S t a r t ************************************************************************* * Name: rapStart * Purpose: Prepares Eisa DMA buffers/Control block for Playing/Recording * This function is called when DMA is Idle. * Returns: 0 = Success or Error number. *************************************************************************/ static int rapStart (uchar_t what) { cardInfo_t *ci; dmaBuf_t *dmaB; dmaCb_t *dmaC; uchar_t stereo; int err; ci = &cardInfo; stereo = (ci->ci_state & CARD_STEREO); #ifdef DEBUG cmn_err (CE_NOTE, "rapStart: Starting %s, full = %d, empty = %d", (what == DI_DMA_PLAYING ? "Playback(D/A)":"Record(A/D)"), ci->ri_full, ci->ri_free ); #endif /* clear Dma buffers */ ci->di_which = 0; rapZeroDma(ci, DMA_BUF_SIZE); /* check for Dma buffer addresses */ if ( (ci->ci_dmaBuf5 == (dmaBuf_t *)0) || (ci->ci_dmaCb5 == (dmaCb_t *)0) ) { cmn_err (CE_WARN, "rapStart: Chan 5 dmaBuf/dmaCb is NULL, what = %d", what); return(EIO); } if ( (ci->ci_dmaBuf6 == (dmaBuf_t *)0) || (ci->ci_dmaCb6 == (dmaCb_t *)0) ) { cmn_err (CE_WARN, "rapStart: Chan 6 dmaBuf/dmaCb is NULL, what = %d", what); return(EIO); } /* * Prepare Eisa Buf and Cb for Channel 5. If in * stereo mode, do the same for Channel 6. */ dmaB = ci->ci_dmaBuf5; dmaC = ci->ci_dmaCb5; rapPrepEisa (dmaB, dmaC, what, dmaLeftPhys ); if ( stereo ) { dmaB = ci->ci_dmaBuf6; dmaC = ci->ci_dmaCb6; rapPrepEisa (dmaB, dmaC, what, dmaRightPhys ); } /* * Program Eisa DMA Channels */ err = eisa_dma_prog (ci->ci_adap, ci->ci_dmaCb5, ci->ci_dmaCh5, EISA_DMA_NOSLEEP); if ( err == 0 ) { cmn_err (CE_WARN, "rapStart: DMA Channel %d is busy", ci->ci_dmaCh5 ); return (EBUSY); } if ( stereo ) { err = eisa_dma_prog (ci->ci_adap, ci->ci_dmaCb6, ci->ci_dmaCh6, EISA_DMA_NOSLEEP); if ( err == 0 ) { cmn_err (CE_WARN, "rapStart: DMA Channel %d is busy", ci->ci_dmaCh6 ); return (EBUSY); } } /* enable hardware recognition on Eisa Dma Channels */ eisa_dma_enable (ci->ci_adap, ci->ci_dmaCb5, ci->ci_dmaCh5, EISA_DMA_NOSLEEP); if ( stereo ) { eisa_dma_enable (ci->ci_adap, ci->ci_dmaCb6, ci->ci_dmaCh6, EISA_DMA_NOSLEEP); } /* set Eisa DMA register for Autoinit mode */ rapSetAutoInit(ci, what); ci->di_state |= what; /* let's do it ! */ if ( what == DI_DMA_PLAYING ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapStart: Starting DMA for D/A Play"); #endif rapStartDA(); } else { #ifdef DEBUG cmn_err (CE_NOTE, "rapStart: Starting DMA for A/D Record"); #endif rapStartAD(); } return(0); } /** End rapStart **/ /************************************************************************ * r a p P r e p E i s a ************************************************************************* * Name: rapPrepEisa * Purpose: prepares EISA Buf and Cb structures. * Returns: None. *************************************************************************/ static void rapPrepEisa( dmaBuf_t *dmaB, dmaCb_t *dmaC, uchar_t what, paddr_t addr) { #ifdef DEBUG cmn_err (CE_NOTE, "rapPrepEisa: Preparing Eisa DMA buffers for %s", (what == DI_DMA_PLAYING ? "Playback(D/A)" : "Record(A/D)" ) ); #endif /* prepare Eisa DMA Buf struct */ bzero (dmaB, sizeof(dmaBuf_t) ); dmaB->count = DMA_BUF_SIZE; dmaB->address = addr; /* prepare Eisa DMA Control Block */ bzero (dmaC, sizeof(dmaCb_t) ); dmaC->reqrbufs = dmaB; dmaC->reqr_path = EISA_DMA_PATH_16; dmaC->trans_type = EISA_DMA_TRANS_DMND; dmaC->targ_step = EISA_DMA_STEP_INC; dmaC->bufprocess = EISA_DMA_BUF_SNGL; if ( what == DI_DMA_PLAYING ) dmaC->cb_cmd = EISA_DMA_CMD_READ; /* mem -> rap10 */ else dmaC->cb_cmd = EISA_DMA_CMD_WRITE; /* rap10 -> mem */ } /*** End rapPrepEisa ***/ /************************************************************************* * r a p S t a r t D A ************************************************************************* * Name: rapStartDA * Purpose: Enables appropriate RAP interrupts and starts D/A Dma. * Returns: None *************************************************************************/ static void rapStartDA() { cardInfo_t *ci; caddr_t addr; ushort_t gpdi, gpis, gpst, dtcd, mask; uchar_t gpcm, pwmd, adcm, dacm; uchar_t stereo; int s; ci = &cardInfo; addr = ci->ci_addr[0]; stereo = ci->ci_state & CARD_STEREO; #ifdef DEBUG cmn_err (CE_NOTE, "rapStartDA: Starting D/A Dma, full = %d, empty = %d", ci->ri_full, ci->ri_free ); #endif /* * Prepare the board for Record (A/D) * Here is what we will do (in exact order): * * GPDI: Stereo = 0xA800, Mono = 0x9800 * itc = 1, dma transfer match count * Stereo: Drq1->Dma5, Drq0->Dma6 * Mono: Drq1->Dma5 * Dt1, Dt0 = 10, Chan 1 ->Drq1, Chan 0 ->Drq0 * Left Chan->Drq1, Right Chan->Drq0 * * DACM: Stereo: BF, Mono: BE * scc = 1, Dma size in byte * ts1 = ts2 = 1, transfer size of 4096 bytes * dl1 = dl0 = 1; Data length of 16 bits for both Channels. * Stereo ? ds1 = ds0 = 1 Start D/A on both Channels. * Mono ? ds1 = 1 Start D/A on Channel 1 * * GPCM: Select Mike level = 0x04 * Aux level = 0x08 * PWMD: 0xFF (Max level) */ gpdi = (stereo ? 0xA800: 0x9800); dacm = (stereo ? 0xBF:0xBE); gpcm = 0x04; pwmd = 0xFF; mask = (stereo ? (GPIS_DN1|GPIS_DN0): GPIS_DN1); #ifdef DEBUG cmn_err (CE_NOTE, "rapStartDA: gpdi = %x, dacm = %x", gpdi, dacm); #endif /* Set Rap-10 card */ OUTB(addr+GPCM, gpcm); OUTB(addr+PWMD, pwmd); OUTW(addr+GPDI, gpdi); OUTB(addr+DACM, dacm); /* * Busy-wait for both Note_On interrupts * The interrupt version is commenetd out for now. */ gpis = INPW(addr+GPIS); #ifdef DEBUG cmn_err (CE_NOTE, "rapStartDA: Waiting for Note_On, gpis = %x, mask = %x", gpis, mask); #endif while ( !(gpis & mask) ) { gpis = INPW(addr+GPIS); #ifdef DEBUG cmn_err (CE_NOTE, "rapStartDA: Waiting ..new gpis = %x", gpis); #endif } #ifdef DEBUG cmn_err (CE_NOTE, "rapStartDA: Note_On Interrupt Received, gpis = %x", gpis ); #endif rapNoteOn(ci, gpis); } /*** End rapStartDA ***/ /************************************************************************* * r a p S t a r t A D ************************************************************************* * Name: rapStartAD * Purpose: Enables appropriate RAP interrupts and starts A/D Dma. * Returns: None *************************************************************************/ static void rapStartAD() { cardInfo_t *ci; caddr_t addr; ushort_t gpdi; uchar_t gpcm, pwmd, adcm, dacm; uchar_t stereo, mic; ci = &cardInfo; addr = ci->ci_addr[0]; stereo = ci->ci_state & CARD_STEREO; #ifdef DEBUG cmn_err (CE_NOTE, "rapStartAD: Starting A/D Dma in %s, full = %d, empty = %d", (stereo ? "Stereo":"Mono"), ci->ri_full, ci->ri_free ); #endif /* * Prepare the board for Record (A/D) * Here is what we will do (in exact order): * * GPDI: Stereo = 0xA400, Mono = 0x9400 * itc = 1, dma transfer match count * Stereo: Drq1->Dma5, Drq0->Dma6 * Mono: Drq1->Dma5 * Dt1, Dt0 = 01, Left Chan->Drq1, Right Chan->Drq0 * * DACM: 0xB0 * scc = 1, Dma size in byte * ts1 = ts2 = 1, transfer size of 4096 bytes * * GPCM: Select Mic level = 0x04 * Aux level = 0x08 * PWMD: 0xFF (Max level) * * ADCM: Stereo: Mic 0x6F, line 0x4F, * Mono: Mic 0x6D, line 0x4D * Mon = 1, Monitor ON * Gin = 1, Head Amp Gain to Mic. * Af1, Af0 = 01, 22.05 KHz * Adl = 1, 16 bit data length * Stereo, Adm = 1, else = 0 * Ads = 1, Start A/D */ gpdi = (stereo ? 0xA400: 0x9400); gpcm = 0x08; adcm = (stereo ? 0x6F:0x6D); dacm = 0xB0; gpcm = 0x04; pwmd = 0xFF; #ifdef DEBUG cmn_err (CE_NOTE, "rapStartAD: Rap init as: gpdi = %x, dacm = %x, gpcm = %x, adcm = %x", gpdi, dacm, gpcm, adcm); #endif OUTW(addr+GPDI, gpdi); OUTB(addr+DACM, dacm); OUTB(addr+GPCM, gpcm); OUTB(addr+PWMD, pwmd); OUTB(addr+ADCM, adcm); } /*** End rapStartAD ***/ /************************************************************************* * r a p B u f T o D m a ************************************************************************* * Name: rapBufToDma * Purpose: moves data from current rwQue[] entry to DMA buffers. * This routine is called by INterrupt handler only except * once before we startd D/A (when no DMA is programmed yet) * Returns: None *************************************************************************/ static void rapBufToDma( int bytes) { cardInfo_t *ci; rwBuf_t *rw; uchar_t *dmaR; uchar_t *dmaL; uchar_t stereo; int i, j, s; ci = &cardInfo; rw = &rwQue[ci->di_idx]; stereo = ci->ci_state & CARD_STEREO; /* * filling 1st half or 2nd half of the buffers ? */ if ( ci->di_which ) { dmaR = &dmaRight[DMA_HALF_SIZE]; dmaL = &dmaLeft[DMA_HALF_SIZE]; if ( bytes == DMA_BUF_SIZE ) { bytes = DMA_HALF_SIZE; } } /* filling 1st half of dma buffers */ else { dmaR = &dmaRight[0]; dmaL = &dmaLeft[0]; } #ifdef DEBUG cmn_err (CE_NOTE, "rapBufToDma: Bytes = %d, which = %d, Idx = %d, rw_count = %d, Full = %d, Empty = %d", bytes, ci->di_which, ci->di_idx, rw->rw_count, ci->ri_full, ci->ri_free); #endif /* * if buffer is not Full, we zero out dma buffers and * return. We cannot wait till it gets Full. */ if ( !(rw->rw_state & RW_FULL) ) { rapZeroDma(ci, bytes); ci->di_ptr = NULL; #ifdef DEBUG cmn_err (CE_NOTE, "rapBufToDma: Buf %d is not Full, rw_state = %x", rw->rw_idx, rw->rw_state ); #endif return; } /* buffer is full of data ..readjust the buffer pointer */ if ( ci->di_ptr == NULL ) ci->di_ptr = rw->rw_buf; /* * Fill buffers ... */ for ( i = 0; i < bytes; i++ ) { /* * First check if buffer is empty. If it is, mark it * as empty, wake anyone up who wants it and get the * next full buffer. */ if ( rw->rw_count <= 0 ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapBufToDma: Buf %d is Empty now, rw_count = %d", rw->rw_idx, rw->rw_count ); #endif rapMarkBuf(rw, ci, RW_EMPTY); ci->di_ptr = NULL; if ( rw->rw_state & RW_WANTED_EMPTY ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapBufToDma: Buf %d is Wanted_Empty", rw->rw_idx ); #endif rw->rw_state &= ~RW_WANTED_EMPTY; wakeup(rw); } j = rapGetNextFull (ci->di_idx, FROM_INTR); if ( j == -1 ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapBufToDma: Could not get next Full buffer"); #endif break; } ci->di_idx = j; rw = &rwQue[ci->di_idx]; ci->di_ptr = rw->rw_buf; continue; } /* buffer still has some data ..move them */ if ( stereo ) { dmaL[i] = *(ci->di_ptr++); dmaR[i] = *(ci->di_ptr++); rw->rw_count -= 2; } else { dmaL[i] = *(ci->di_ptr++); rw->rw_count--; } } /* for .. */ /* Flush the cache line so that Dma buffers contain all data */ dki_dcache_wbinval (dmaL, (unsigned)bytes); if ( stereo ) dki_dcache_wbinval (dmaR, (unsigned)bytes); } /*** end rapBufToDma ***/ /************************************************************************* * r a p D m a T o B u f ************************************************************************* * Name: rapDmaToBuf * Purpose: Moves data from DMA buffers (Right and Left in stereo) * into a rwQue entry. This routine is called only by * Interrupt Handler. * Returns: None *************************************************************************/ static void rapDmaToBuf( int bytes) { cardInfo_t *ci; rwBuf_t *rw; uchar_t *dmaR; uchar_t *dmaL; uchar_t stereo; int i, j, s, inc; ci = &cardInfo; rw = &rwQue[ci->di_idx]; stereo = ci->ci_state & CARD_STEREO; /* * filling 1st half or 2nd half of the buffers ? */ if ( ci->di_which ) { dmaR = &dmaRight[DMA_HALF_SIZE]; dmaL = &dmaLeft[DMA_HALF_SIZE]; if ( bytes == DMA_BUF_SIZE ) { bytes = DMA_HALF_SIZE; } } /* filling 1st half of dma buffers */ else { dmaR = &dmaRight[0]; dmaL = &dmaLeft[0]; } /* Invalidate the Cache */ dki_dcache_inval (dmaL, (unsigned)bytes); if ( stereo ) dki_dcache_inval (dmaR, (unsigned)bytes); #ifdef DEBUG cmn_err (CE_NOTE, "rapDmaToBuf: Bytes= %d, Idx = %d, rw_count = %d, Full = %d, Empty= %d", bytes, ci->di_idx, rw->rw_count, ci->ri_full, ci->ri_free); #endif /* * if buffer is Full ..we cannot wait ! Ignore new data * by simply returning. */ if ( rw->rw_state & RW_FULL ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapDmaToBuf: Buf %d is not Empty ..Ignoring data", rw->rw_idx ); #endif return; } /* buffer is Empty ..calculate the end address */ if ( ci->di_ptr == NULL ) ci->di_ptr = rw->rw_buf; #ifdef DEBUG cmn_err (CE_NOTE, "rapDmaToBuf: Moving %s of DMA buffers in %s, rw_count = %x", (ci->di_which ? "Second Half" : "First Half"), (stereo ? "Stereo":"Monoe"), rw->rw_count); #endif /* * Fill buffers ...in stereo bytes are Left:Right:Left:Right... */ for ( i = 0; i < bytes; i++ ) { /* * First check if this buffer is Full or not. * If it is, mark it as Full and wake anyone up who is * waiting for it. Then get the next Empty buffer. */ if ( rw->rw_count >= RW_BUF_SIZE ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapDmaToBuf: Buf %d is Full now, rw_count = %d", rw->rw_idx, rw->rw_count ); #endif rapMarkBuf(rw, ci, RW_FULL); if ( rw->rw_state & RW_WANTED_FULL ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapDmaToBuf: Buf %d is Wanted_Full", rw->rw_idx ); #endif rw->rw_state &= ~RW_WANTED_FULL; wakeup(rw); } j = rapGetNextEmpty(ci->di_idx, FROM_INTR); if ( j == -1 ) { cmn_err (CE_NOTE, "rapDmaToBuf: Could not get next empty"); return; } ci->di_idx = j; rw = &rwQue[ci->di_idx]; ci->di_ptr = rw->rw_buf; continue; } /* buffer still has room ...move data */ if ( stereo ) { *(ci->di_ptr++) = dmaL[i]; *(ci->di_ptr++) = dmaR[i]; rw->rw_count += 2; } else { *(ci->di_ptr++) = dmaL[i]; rw->rw_count++; } } /* while bytes ... */ } /*** end rapDmaToBuf ***/ /************************************************************************* * r a p G e t N e x t F u l l ************************************************************************* * Name: rapGetNextFull * Purpose: returns the index of next Full entry in rwQue[], * starting from a given index. Sleeps if the entry * is not Full. * Returns: the index of the empty entry. *************************************************************************/ static short rapGetNextFull (short idx, uchar_t fromIntr) { cardInfo_t *ci; int s; toid_t to_id; rwBuf_t *rw; ci = &cardInfo; #ifdef DEBUG cmn_err (CE_NOTE, "rapGetNextFull: Getting Next Full Buffer..idx = %d, fromIntr: %d", idx, fromIntr ); #endif /* go to beginning if at the end of the queu */ idx++; if ( idx >= RW_BUF_COUNT ) idx = 0; rw = &rwQue[idx]; /* * if buffer is not available and we were called from Intrupt * handler, simply ignore the request and return error */ s = LOCK(); if ( !(rw->rw_state & RW_FULL) && (fromIntr) ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapGetNextFull: Buffer %d is not Full. ..Cannot Wait", rw->rw_idx); #endif UNLOCK(s); return(-1); } /* wait for the buffer to become Full */ if ( !(rw->rw_state & RW_FULL) ) { ci->ri_tout = 0; to_id = itimeout (rapTimeOut, rw, RW_TIMEOUT, plbase, 0, 0, 0); while ( !(rw->rw_state & RW_FULL) && !ci->ri_tout ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapGetNextFull: Waiting for Buf %d to become Full", rw->rw_idx ); #endif rw->rw_state |= RW_WANTED_FULL; if ( sleep(rw, PUSER | PCATCH) ) { untimeout(to_id); #ifdef DEBUG cmn_err (CE_NOTE, "rapGetNextFull: Interrupted"); #endif rw->rw_state &= ~RW_WANTED_FULL; UNLOCK(s); return(-1); } } untimeout (to_id); if ( ci->ri_tout ) { #ifdef DEBUG cmn_err (CE_NOTE, "raGetNextFull: Timed out"); #endif rw->rw_state &= ~RW_WANTED_FULL; UNLOCK(s); return (-1); } } /* if !(rw->rw_state & RW_FULL) */ UNLOCK(s); #ifdef DEBUG cmn_err (CE_NOTE, "rapGetNextFull: next Full Buffer is %d", idx); #endif return(idx); } /*** End rapGetNextFull ***/ /************************************************************************* * r a p G e t N e x t E m p t y ************************************************************************* * Name: rapGetNextEmpty * * Purpose: returns the index of next empty entry in rwQue[], * starting from a given index. Sleeps if the entry * is not empty. * Returns: the index of the empty entry. *************************************************************************/ static short rapGetNextEmpty (short idx, uchar_t fromIntr) { cardInfo_t *ci; int s; toid_t to_id; rwBuf_t *rw; ci = &cardInfo; #ifdef DEBUG cmn_err (CE_NOTE, "rapGetNextEmpty: Getting Next Empty Buffer..idx = %d, fromIntr: %d", idx, fromIntr ); #endif /* go to beginning if at the end of the queu */ idx++; if ( idx >= RW_BUF_COUNT ) idx = 0; rw = &rwQue[idx]; s = LOCK(); /* * if buffer is nit available and we were called from Intrupt * handler, simply ignore the request and return error */ if ( (rw->rw_state & RW_FULL) && (fromIntr) ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapGetNextEmpty: Buffer %d is not Empty ..Cannot Wait", rw->rw_idx); #endif UNLOCK(s); return(-1); } /* wait for the buffer to become Empty */ if ( rw->rw_state & RW_FULL ) { ci->ri_tout = 0; to_id = itimeout (rapTimeOut, rw, RW_TIMEOUT, plbase, 0, 0, 0); while ( (rw->rw_state & RW_FULL) && !ci->ri_tout ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapGetNextEmpty: Waiting for Buf %d to become Empty", rw->rw_idx ); #endif rw->rw_state |= RW_WANTED_EMPTY; if ( sleep(rw, PUSER | PCATCH) ) { untimeout(to_id); #ifdef DEBUG cmn_err (CE_NOTE, "rapGetNextEmpty: Interrupted"); #endif rw->rw_state &= ~RW_WANTED_EMPTY; UNLOCK(s); return(-1); } } /* while .. */ untimeout (to_id); if ( ci->ri_tout ) { #ifdef DEBUG cmn_err (CE_NOTE, "raGetNextEmpty: Timed out"); #endif rw->rw_state &= ~RW_WANTED_EMPTY; UNLOCK(s); return (-1); } } /* if (rw->rw_state & RW_FULL) */ UNLOCK(s); #ifdef DEBUG cmn_err (CE_NOTE, "rapGetNextEmpty: next Empty Buffer is %d", idx); #endif return(idx); } /*** End rapGetNextEmpty ***/ /************************************************************************* * r a p D i s I n t ************************************************************************* * Name: rapDisInt * Purpose: Disables RAP-10 interrupts. * Returns: None. *************************************************************************/ static void rapDisInt( cardInfo_t *ci) { caddr_t addr; ushort_t s; uchar_t c; #ifdef DEBUG cmn_err (CE_NOTE, "rapDisInt: full = %d, empty = %d, di_state = %d", ci->ri_full, ci->ri_free, ci->di_state ); #endif addr = ci->ci_addr[0]; /* disable all Interrupts */ s = 0; OUTW(addr+GPDI, s); OUTB(addr+DACM, 0x00); OUTB(addr+ADCM, 0x00); #ifdef DEBUG cmn_err (CE_NOTE, "rapDisInt: Rap is set"); #endif } /*** End rapDisInt ***/ /************************************************************************** * r a p G e t D m a * ************************************************************************** * Name: rapGetDma * Purpose: allocates dma Buf and Cb structures * Returns: 0 = Success, 1 = Error **************************************************************************/ static int rapGetDma ( dmaBuf_t **dmaB, dmaCb_t **dmaC, int ch ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapGetDma: Getting Eisa Dma Buf and Cb for Channel %d", ch); #endif *dmaB = eisa_dma_get_buf (EISA_DMA_SLEEP); if ( *dmaB == NULL ) return (1); *dmaC = eisa_dma_get_cb ( EISA_DMA_SLEEP ); if ( *dmaC == NULL ) return (1); return (0); } /*** End rapGetDma ***/ /************************************************************************* * r a p M a r k B u f ************************************************************************* * Name: rapMarkBuf * Purpose: Marks a buffer (Empty, Busy, Full) and increments/decrements * appropriate counters. Buffers status changed as: * Empty -> Busy -> Full -> Empty -> Busy .. * Returns: None. *************************************************************************/ static void rapMarkBuf (rwBuf_t *rw, cardInfo_t *ci, uchar_t m) { int s; s = LOCK(); switch ( m ) { case RW_EMPTY: rw->rw_state &= ~RW_FULL; if ( ci->ri_full ) ci->ri_full--; ci->ri_free++; rw->rw_count = 0; #ifdef DEBUG cmn_err (CE_NOTE, "rapMarkBuf: Buf %d set EMPTY. Full = %d, Emp = %d", rw->rw_idx, ci->ri_full, ci->ri_free ); #endif break; case RW_FULL: rw->rw_state |= RW_FULL; ci->ri_full++; if ( ci->ri_free ) ci->ri_free--; rw->rw_count = RW_BUF_SIZE; #ifdef DEBUG cmn_err (CE_NOTE, "rapMarkBuf: Buf %d set FULL. Full = %d, Emp = %d", rw->rw_idx, ci->ri_full, ci->ri_free ); #endif break; } UNLOCK(s); } /*** End rapMarkBuf ***/ /************************************************************************* * r a p K e r n M e m ************************************************************************* * Name: rapKernMem * Purpose: Allocates/Disallocates Kernel memory for Right and * Left DMA channels. * Returns: 0 = Success, 1 = Failure. *************************************************************************/ static int rapKernMem ( uchar_t what) { #ifdef DEBUG cmn_err (CE_NOTE, "rapKernMem: %s Kernel Contigious Memory", (what == 1 ? "Allocating" : "Deallocating") ); #endif switch ( what ) { /*=======================================* * Allocate Right/Left DMA Channels * *=======================================*/ case 1: dmaRight = kmem_alloc (DMA_BUF_SIZE, KM_NOSLEEP | KM_PHYSCONTIG | KM_CACHEALIGN ); if ( dmaRight == (caddr_t)NULL ) { cmn_err (CE_WARN, "rapKernMem: Cannot allocate DMA memory for R_chann"); return(1); } dmaLeft = kmem_alloc (DMA_BUF_SIZE, KM_NOSLEEP | KM_PHYSCONTIG | KM_CACHEALIGN ); if ( dmaLeft == (caddr_t)NULL ) { cmn_err (CE_WARN, "rapKernMem: Cannot allocate DMA memory for L_chann"); kmem_free (dmaRight, DMA_BUF_SIZE); return(1); } /* get the physicall address */ dmaRightPhys = kvtophys(dmaRight); dmaLeftPhys = kvtophys(dmaLeft); return(0); /*=======================================* * Deallocate Right/Left DMA Channels * *=======================================*/ case 2: if ( dmaRight != NULL ) { kmem_free (dmaRight, DMA_BUF_SIZE); dmaRight = (caddr_t)NULL; } if ( dmaLeft != NULL ) { kmem_free (dmaLeft, DMA_BUF_SIZE); dmaLeft = (caddr_t)NULL; } return(0); } /* switch */ } /*** End rapKernMem ***/ /************************************************************************* * r a p T i m e O u t ************************************************************************* * Name: rapTimeOut * Purpose: is called when Read/Write waiting for buffers time out. * Returns: *************************************************************************/ static void rapTimeOut( void *addr ) { cardInfo_t *ci; ci = &cardInfo; /* indicate a timeout */ ci->ri_tout = 1; wakeup (addr); } /************************************************************************* * r a p N o t e O n ************************************************************************* * Name: rapNoteOn * Purpose: Sends a MIDI Note_On message. * This code is taken from RAP-10 manual. * Returns: None. *************************************************************************/ static void rapNoteOn ( cardInfo_t *ci, ushort_t orig_gpis) { int s, stereo; uchar_t c, pan, rank, chksum, sum; caddr_t addr; ushort_t gpis; addr = ci->ci_addr[0]; stereo = ci->ci_state & CARD_STEREO; pan = 0x40; rank = 0x01; /* for 22050 Hz */ gpis = orig_gpis; /* * Busy wait till Txd Fifo is empty * The interrupt version is commenetd out below */ #ifdef DEBUG cmn_err (CE_NOTE, "rapNoteOn: Waiting for Txd Fifo Empty, gpis = %x", gpis); #endif while ( !(gpis & GPIS_TXD) ) { gpis = INPW(addr+GPIS); #ifdef DEBUG cmn_err (CE_NOTE, "rapNoteOn: Waiting ..new gpis = %x", gpis); #endif } #ifdef DEBUG cmn_err (CE_NOTE, "rapNoteOn: Issuing a Note_On SysEx Cmd"); #endif /* send Note_On */ c = 0xf0; OUTB(addr+MDTD, c); c = 0x41; OUTB(addr+MDTD, c); c = 0x10; OUTB(addr+MDTD, c); c = 0x56; OUTB(addr+MDTD, c); c = 0x12; OUTB(addr+MDTD, c); if ( stereo ) { c = 0x03; OUTB(addr+MDTD, c); c = 0x00; OUTB(addr+MDTD, c); c = 0x01; OUTB(addr+MDTD, c); sum = 0x03 + 0x01; } else { c = 0x02; OUTB(addr+MDTD, c); c = 0x00; OUTB(addr+MDTD, c); c = 0x0A+0x01; OUTB(addr+MDTD, c); sum = 0x02+0x0A+0x01; } c = 0x01; OUTB(addr+MDTD, c); c = 0x7F; OUTB(addr+MDTD, c); c = 0x7F; OUTB(addr+MDTD, c); OUTB(addr+MDTD, rank); sum += (0x01+0x7F+0x7F+rank); c = 0x40; OUTB(addr+MDTD, c); c = 0x00; OUTB(addr+MDTD, c); c = 0x40; OUTB(addr+MDTD, c); OUTB(addr+MDTD, pan); sum += (0x40+0x40+pan); /* calculate the checksum */ chksum = (0x80 - (sum % 0x80)) & 0x7F; OUTB(addr+MDTD, chksum); c = 0xF7; OUTB(addr+MDTD, c); #ifdef DEBUG cmn_err (CE_NOTE, "rapNoteOn: Note_On Issued, chksum = %x", chksum); #endif } /* end rapNoteOn */ /************************************************************************* * r a p N o t e O f f ************************************************************************* * Name: rapNoteOff * Purpose: Sends a MIDI Note_Off message. * This code is taken from RAP-10 manual. * Returns: None. *************************************************************************/ static void rapNoteOff ( cardInfo_t *ci) { int s, stereo; uchar_t pan, b, rank, sum, chksum; caddr_t addr; ushort_t gpis; addr = ci->ci_addr[0]; stereo = ci->ci_state & CARD_STEREO; pan = 0x40; rank = 0x01; /* for 22050 Hz */ #ifdef DEBUG cmn_err (CE_NOTE, "rapNoteOff: Waiting for Txd Empty"); #endif /* wait till Txd is Empty */ gpis = INPW(addr+GPIS); while ( !(gpis & GPIS_TXD) ) { us_delay(10); gpis = INPW(addr+GPIS); #ifdef DEBUG cmn_err (CE_NOTE, "rapNoteOff: Waiting ..new gpis = %x", gpis); #endif } #ifdef DEBUG cmn_err (CE_NOTE, "rapNoteOff: Issuing Note_Off"); #endif /* send Note_On */ OUTB(addr+MDTD, 0xF0); OUTB(addr+MDTD, 0x41); OUTB(addr+MDTD, 0x10); OUTB(addr+MDTD, 0x56); OUTB(addr+MDTD, 0x12); if ( stereo ) { OUTB(addr+MDTD, 0x03); OUTB(addr+MDTD, 0x00); OUTB(addr+MDTD, 0x01); sum = 0x03 + 0x01; } else { OUTB(addr+MDTD, 0x02); OUTB(addr+MDTD, 0x00); OUTB(addr+MDTD, 0x0A+0x01); sum = 0x02 + 0x0A + 0x01; } OUTB(addr+MDTD, 0x00); OUTB(addr+MDTD, 0x7F); OUTB(addr+MDTD, 0x7F); OUTB(addr+MDTD, 0x00); sum += 0x7F + 0x7F; OUTB(addr+MDTD, 0x40); OUTB(addr+MDTD, 0x00); OUTB(addr+MDTD, 0x40); OUTB(addr+MDTD, pan); sum += 0x40 + 0x40 + pan; /* calculate checksum */ chksum = (0x80 - (sum % 0x80)) & 0x7F; OUTB(addr+MDTD, chksum); OUTB(addr+MDTD, 0x7F); #ifdef DEBUG cmn_err (CE_NOTE, "rapNoteOff: Note_On Issued, chksum = %x", chksum); #endif } /* end rapNoteOff */ /************************************************************************* * r a p Z e r o D m a ************************************************************************* * Name: rapZeroDma * Purpose: Zero outs DMA buffers. * Returns: None. *************************************************************************/ static void rapZeroDma (cardInfo_t *ci, int bytes) { caddr_t dmaL, dmaR; int stereo, s; s = LOCK(); stereo = ci->ci_state & CARD_STEREO; /* * Zero out which half ? */ if ( ci->di_which ) { dmaR = &dmaRight[DMA_HALF_SIZE]; dmaL = &dmaLeft[DMA_HALF_SIZE]; if ( bytes == DMA_BUF_SIZE ) { bytes = DMA_HALF_SIZE; } } /* Zer out 1st half of dma buffers */ else { dmaR = &dmaRight[0]; dmaL = &dmaLeft[0]; } #ifdef DEBUG cmn_err (CE_NOTE, "rapZeroDma: Zeroing out %s of Dma buffers in %s for %d bytes", (ci->di_which ? "2nd half":"1st half"), (stereo ? "Stereo":"Mono"), bytes); #endif bzero (dmaL, bytes); dki_dcache_wbinval (dmaL, (unsigned)bytes); if ( stereo ) { bzero (dmaR, bytes); dki_dcache_wbinval (dmaR, (unsigned)bytes); } UNLOCK(s); } /*** end rapZeroDma ***/ /************************************************************************* * r a p R e l e a s e D m a ************************************************************************* * Name: rapReleaseDma * Purpose: Releases Dma channel(s). * Note that we access kernel's Dma structure and later on * a routine will be provided for us to avoid this. * Returns: None. *************************************************************************/ static void rapReleaseDma (cardInfo_t *ci) { /* disable Eisa Dma */ #ifdef DEBUG cmn_err (CE_NOTE, "rapReleaseDma: Releasing Eisa Dma Chann %d", ci->ci_dmaCh5); #endif eisa_dma_disable(0, ci->ci_dmaCh5); if ( ci->ci_state & CARD_STEREO ) { #ifdef DEBUG cmn_err (CE_NOTE, "rapReleaseDma: Releasing Eisa Dma Chann %d", ci->ci_dmaCh6); #endif eisa_dma_disable(0, ci->ci_dmaCh6); } } /*** end rapReleaseDma ***/ /************************************************************************* * r a p S e t A u t o I n i t ************************************************************************* * Name: rapSetAutoInit * Purpose: sets Eisa DMA register for Autoinit. In Autoinit, DMA * starts over from the beginning of the buffer again once it * has transfered all bytes in the buffer. * Returns: None. *************************************************************************/ #define EISA_MODE_REG 0xd6 #define EISA_CH5 0x01 #define EISA_CH6 0x02 #define EISA_WRITE 0x04 #define EISA_READ 0x08 #define EISA_AUTO 0x10 static void rapSetAutoInit( cardInfo_t *ci, uchar_t what) { uchar_t b; #ifdef DEBUG cmn_err (CE_NOTE, "rapSetAutoInit: setting Autoinit DMA for %s, Eisa Addr = %x", ( what == DI_DMA_PLAYING ? "Playback(D/A)" : "Record(A/D)" ), eisa_addr ); #endif b = 0; if ( what == DI_DMA_PLAYING ) b |= EISA_READ; /* Memory -> Device */ else b |= EISA_WRITE; /* Device -> Memory */ /* Autoinit for Channel 5 - Demand Mode select is default */ b |= (EISA_AUTO | EISA_CH5); OUTB(eisa_addr+EISA_MODE_REG, b); /* Autoinit for Channel 6 (if in stereo mode) */ if ( ci->ci_state & CARD_STEREO ) { b &= ~EISA_CH5; b |= EISA_CH6; OUTB(eisa_addr+EISA_MODE_REG, b); } } /*** End rapSetAutoInit ***/